package co.baiku.boot.common.tools;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 反射工具类
 *
 * @author 李永曜
 * @time 2014-4-25下午01:20:39
 */
public class ReflectTools {
    private static final Logger log = LoggerFactory.getLogger(ReflectTools.class);

    private ReflectTools() {
    }

    /**
     * 获得属性名
     *
     * @param object
     * @return
     */
    public static List<String> componentsName(Object object) {
        Field[] fields = object.getClass().getDeclaredFields();
        List<String> filedName = new ArrayList<>();
        for (int i = 0; i < fields.length; i++) {
            String varName = fields[i].getName();
            boolean accessFlag = fields[i].isAccessible();
            fields[i].setAccessible(true);
            filedName.add(varName);
            fields[i].setAccessible(accessFlag);

        }
        return filedName;
    }

    /**
     * 获得属性类型
     *
     * @param field,clazz
     * @return
     */
    public static Class<?> componentsType(String field, Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        Class<?> type = null;
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(field)) {
                boolean accessFlag = fields[i].isAccessible();
                fields[i].setAccessible(true);
                type = fields[i].getType();
                fields[i].setAccessible(accessFlag);
            }
        }
        return type;
    }

    /**
     * 获取键值对
     *
     * @param object
     * @return
     */
    public static Map<String, Object> componentsNameAndValue(Object object) {
        Map<String, Object> nameAndValue = new HashMap<>();
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            List<String> filedName = new ArrayList<>();
            List<Object> filedValue = new ArrayList<>();
            for (Field field : fields) {
                boolean accessFlag = field.isAccessible();
                field.setAccessible(true);
                String varName = field.getName();
                Object varValue = field.get(object);
                filedName.add(varName);
                filedValue.add(varValue);
                field.setAccessible(accessFlag);
            }
            nameAndValue.put("value", filedValue);
            nameAndValue.put("key", filedName);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return nameAndValue;
    }

    /**
     * 获取键值对
     *
     * @param object
     * @return
     */
    public static Map<String, Object> componentsNameAndValueByMethod(Object object) {
        Map<String, Object> nameAndValue = new HashMap<>();
        try {
            Method[] fields = object.getClass().getDeclaredMethods();
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                String varName = fields[i].getName();
                if (varName.startsWith("get")) {
                    String name = varName.substring(3);
                    name = String.valueOf(name.charAt(0)).toLowerCase() + name.substring(1);
                    try {
                        Object value = fields[i].invoke(object);
                        nameAndValue.put(name, value);
                    } catch (InvocationTargetException e) {
                        log.error(e.getMessage(), e);
                    }
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return nameAndValue;
    }

    /**
     * 根据属性名称查找对象中的相应值
     *
     * @param name   属性名
     * @param object 要查找的对象
     * @return Object 返回属性对应的值
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws NoSuchFieldException
     * @throws SecurityException
     */
    public static Object getValueByName(String name, Object object)
            throws IllegalArgumentException, IllegalAccessException,
            SecurityException {
        Field f = null;
        try {
            f = object.getClass().getDeclaredField(name);
        } catch (NoSuchFieldException e) {
            log.error(e.getMessage(), e);
        }
        if (f == null) {
            return null;
        }
        boolean accessFlag = f.isAccessible();
        f.setAccessible(true);
        Object o = f.get(object);
        f.setAccessible(accessFlag);
        return o;
    }

    /**
     * 设置对象的某个属性的值
     *
     * @param name   属性名
     * @param value  值
     * @param object 对象
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws SecurityException
     * @throws NoSuchFieldException
     */
    public static Object setValueByName(String name, Object value, Object object)
            throws IllegalArgumentException, IllegalAccessException,
            SecurityException, NoSuchFieldException {
        Field f = object.getClass().getDeclaredField(name);
        return setValueByName(f, value, object);
    }

    /**
     * 设置对象的某个属性的值
     *
     * @param field  属性
     * @param value  值
     * @param object 对象
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws SecurityException
     * @throws NoSuchFieldException
     */
    public static Object setValueByName(Field field, Object value, Object object)
            throws IllegalArgumentException, IllegalAccessException,
            SecurityException {
        boolean accessFlag = field.isAccessible();// 获得原始权限
        field.setAccessible(true);// 权限设置为可访问
        field.set(object, value);
        field.setAccessible(accessFlag);// 还原权限
        return object;
    }

    /**
     * 通过方法赋值
     *
     * @param field
     * @param value
     * @param object
     * @return
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public static Object setValueByNameInokeMethod(Field field, Object value, Object object) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method method = object.getClass().getMethod("set" + StringTools.firstLetterToUpper(field.getName()), field.getType());
        method.invoke(object, value);
        return object;
    }

    /**
     * 通过方法取值
     *
     * @param field
     * @param object
     * @return
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public static Object getValueByNameInokeMethod(Field field, Object object) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method method = object.getClass().getMethod("get" + StringTools.firstLetterToUpper(field.getName()));
        return method.invoke(object);
    }

    /**
     * 获得所有成员变量，包括所有超类
     *
     * @param clazz
     * @return
     */
    public static List<Field> getAllFields(Class<?> clazz) {
        List<Field> result = new ArrayList<Field>();
        if (clazz == null) {
            return result;
        }
        for (Field f : clazz.getDeclaredFields()) {
            result.add(f);
        }
        result.addAll(getAllFields(clazz.getSuperclass()));
        return result;
    }


    public static Object getValue(Field f, Object object) {
        try {
            boolean accessFlag = f.isAccessible();
            f.setAccessible(true);
            Object o = f.get(object);
            f.setAccessible(accessFlag);
            return o;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    public static Map<String, Object> toMap(Object object) {
        Map<String, Object> result = new HashMap<>();
        List<Field> allFields = getAllFields(object.getClass());
        for (Field field : allFields) {
            result.put(field.getName(), getValue(field, object));
        }
        return result;
    }
}
